package com.gitee.wsl.lang.character

import com.gitee.wsl.ext.string.codePointAt

private const val MIN_SUPPLEMENTARY_CODE_POINT = 0x010000

internal const val MAX_CODE_POINT: Int = 0x10FFFF
internal const val MIN_HIGH_SURROGATE: Int = 0xD800

internal const val MIN_LOW_SURROGATE: Int = 0xDC00

internal const val HIGH_SURROGATE_ENCODE_OFFSET: Int =
    (MIN_HIGH_SURROGATE - (MIN_SUPPLEMENTARY_CODE_POINT ushr 10))

fun CharSequence.forEachCodePoint(f: (Int) -> Unit) {
    var offset = 0
    while (offset < length) {
        val codePoint = codePointAt(offset)
        f(codePoint)
        offset += codePoint.charCount()
    }
}

fun Int.codePointToString(): String {
    return if ( charCount() == 1) {
        toChar().toString()
    }
    else {
        val high = ((this ushr 10) + (MIN_HIGH_SURROGATE - (MIN_SUPPLEMENTARY_CODE_POINT ushr 10))).toChar()
        val low = ((this and 0x3ff) + MIN_LOW_SURROGATE).toChar()
        charArrayOf(high, low).concatToString()
    }
}


fun Int.charCount(): Int = if (this >= MIN_SUPPLEMENTARY_CODE_POINT) 2 else 1

internal fun toCodePoint(high: Char, low: Char): Int {
    // Optimized form of:
    // return ((high - MIN_HIGH_SURROGATE) << 10)
    //         + (low - MIN_LOW_SURROGATE)
    //         + MIN_SUPPLEMENTARY_CODE_POINT;
    return (high.code shl 10) + low.code + (MIN_SUPPLEMENTARY_CODE_POINT
            - (Char.MIN_HIGH_SURROGATE.code shl 10)
            - Char.MIN_LOW_SURROGATE.code)
}

fun Char.Companion.isSupplementaryCodePoint(codePoint: Int): Boolean =
    codePoint in MIN_SUPPLEMENTARY_CODE_POINT..MAX_CODE_POINT


internal fun isBmpCodePoint(codePoint: Int): Boolean =
    codePoint ushr 16 == 0

internal fun highSurrogate(codePoint: Int): Char =
    ((codePoint ushr 10) + HIGH_SURROGATE_ENCODE_OFFSET).toChar()

internal fun lowSurrogate(codePoint: Int): Char =
    ((codePoint and 0x3FF) + MIN_LOW_SURROGATE).toChar()

internal fun isValidCodePoint(codePoint: Int): Boolean =
    codePoint in 0..MAX_CODE_POINT